﻿#region Copyright and Licensing Notice
// Copyright (C) 2011 Steven Bosnick
// 
// This program is open source software; you can redistribute it 
// and/or modifiy it under the terms of the Eclipse Public License,
// version 1.0.
// 
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A 
// PARTICULAR PURPOSE. See the Eclipse Public License for more
// details.
// 
// You should have received a copy of the Eclipse Public License 
// along with this program; if not, you can find a copy at
// "http://www.opensource.org/licenses/eclipse-1.0.php".
#endregion
using System;
using System.Collections.ObjectModel;
using System.Linq;
using BitwiseOre.Properties;
using BitwiseOre.SharedMemory.Core.Common;
using BitwiseOre.SharedMemory.JetBrains;

namespace BitwiseOre.SharedMemory.Core.Primatives
{
    [UsedImplicitly]
    internal sealed class ConnectionBuffersFactory : ISharedFactory<ConnectionBuffers>, ISharedObjectInit
    {
        private static readonly string UnititializedCountMessage = Resources.ConnectionBuffersFactory_UnititializedCountMessage;
        private static readonly string UnititializedBufferSizeMessage = Resources.ConnectionBuffersFactory_UnititializedBufferSizeMessage;
        private static readonly string UnexpectedHeaderFoundMessage = Resources.ConnectionBuffersFactory_UnexpectedHeaderFoundMessage;

        private readonly IAllocator _allocator;
        private readonly Func<IIntArray, int, IByteArray, IBufferLayout> _createBufferLayout;
        private readonly Func<IBufferLayout, IReadBuffer> _createReadBuffer;
        private readonly Func<IBufferLayout, IWriteBuffer> _createWriteBuffer;

        private int? _count;
        private int? _bufferSize;
        private bool _readBuffersBeforeWriteBuffers;

        public ConnectionBuffersFactory(IAllocator allocator,
                                        Func<IIntArray, int, IByteArray, IBufferLayout> createBufferLayout,
                                        Func<IBufferLayout, IReadBuffer> createReadBuffer,
                                        Func<IBufferLayout, IWriteBuffer> createWriteBuffer,
                                        int? count = null,
                                        int? bufferSize = null)
        {
            _allocator = allocator;
            _createBufferLayout = createBufferLayout;
            _createReadBuffer = createReadBuffer;
            _createWriteBuffer = createWriteBuffer;
            _count = count;
            _bufferSize = bufferSize;
        }

        public void InitializeNew()
        {
            if (_count == null) throw new InvalidOperationException(UnititializedCountMessage);
            if (_bufferSize == null) throw new InvalidOperationException(UnititializedBufferSizeMessage);

            _allocator.AllocateArray<ConnectionBufferHeader>(1)[0] = new ConnectionBufferHeader(_count.Value,
                                                                                                _bufferSize.Value);
            _readBuffersBeforeWriteBuffers = true;
        }

        public void InitializeExisting()
        {
            var header = _allocator.AllocateArray<ConnectionBufferHeader>(1)[0];
            if (!header.Verify()) throw new InvalidOperationException(UnexpectedHeaderFoundMessage);

            _count = header.Count;
            _bufferSize = header.BufferSize;
            _readBuffersBeforeWriteBuffers = false;
        }

        // ReSharper disable PossibleInvalidOperationException
        public ConnectionBuffers Create()
        {
            var lengthsarrays = new[]
                                    {
                                        _allocator.AllocateIntArray((int) _count),
                                        _allocator.AllocateIntArray((int) _count)
                                    };

            var buffers = (from lengths in lengthsarrays
                           select
                               (from index in Enumerable.Range(0, _count.Value)
                                let contents = _allocator.AllocateByteArray(_bufferSize.Value)
                                select _createBufferLayout(lengths, index, contents))
                               .ToArray())
                .ToArray();


            var readindex = _readBuffersBeforeWriteBuffers ? 0 : 1;
            var writeindex = _readBuffersBeforeWriteBuffers ? 1 : 0;

            var readbuffers = new ReadOnlyCollection<IReadBuffer>(
                buffers[readindex]
                    .Select(_createReadBuffer)
                    .ToList());

            var writebuffers = new ReadOnlyCollection<IWriteBuffer>(
                buffers[writeindex]
                    .Select(_createWriteBuffer)
                    .ToList());

            return new ConnectionBuffers(readbuffers, writebuffers);
        }
        // ReSharper restore PossibleInvalidOperationException
    }

}